package ar.edu.utn.frba.tacs.warnes;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.util.ArrayList;
import java.util.List;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import ar.edu.utn.frba.tacs.warnes.entities.Pedido;
import ar.edu.utn.frba.tacs.warnes.entities.Pieza;
import ar.edu.utn.frba.tacs.warnes.excepciones.NoPuedoVenderPiezaDisponibleException;
import ar.edu.utn.frba.tacs.warnes.excepciones.SoyUnPedidoCanceladoException;
import ar.edu.utn.frba.tacs.warnes.excepciones.SoyUnPedidoVendidoException;
import ar.edu.utn.frba.tacs.warnes.excepciones.YaEstaVendidaException;
import ar.edu.utn.frba.tacs.warnes.pieza.PiezaDisponibleState;
import ar.edu.utn.frba.tacs.warnes.pieza.PiezaVendidaState;
import ar.edu.utn.frba.tacs.warnes.utils.ContenedorWarnes;

public class PedidosTestCase {
	private ContenedorWarnes contenedor;
	private Pieza pieza;
	private Pieza pieza2;
	private Pieza pieza3;

	@Before
	public void inicializaciones() {
		this.contenedor = ContenedorWarnes.getInstance();
		this.pieza = this.contenedor.creaUnaPieza("Llanta");
		this.pieza2 = this.contenedor.creaUnaPieza("Radio");
		this.pieza3 = this.contenedor.creaUnaPieza("Parlante");
	}

	@After
	public void limpiarContenedor() {
		this.contenedor.limpiate();
	}

	@Test
	public void testNuevoPedido() {
		List<Pieza> piezas = new ArrayList<Pieza>();
		piezas.add(this.pieza);
		piezas.add(this.pieza2);
		Pedido pedido = this.contenedor.crearUnPedido(piezas);

		assertTrue(pedido.tienePieza(this.pieza));
		assertTrue(pedido.tienePieza(this.pieza2));
		assertFalse(pedido.tienePieza(this.pieza3));
		assertEquals(2, pedido.getPiezas().size());
	}

	/**
	 * Testea el estado de las piezas de un pedido reservado
	 */
	@Test
	public void testPedidoEnReserva() {
		List<Pieza> piezas = new ArrayList<Pieza>();
		piezas.add(this.pieza);
		piezas.add(this.pieza2);
		@SuppressWarnings("unused")
		Pedido pedido = this.contenedor.crearUnPedido(piezas);

		assertTrue(this.pieza.estaReservada());
		assertTrue(this.pieza2.estaReservada());
		assertTrue(this.pieza3.estaDisponible());
	}

	/**
	 * Testea el estado de las piezas de un pedido vendido
	 */
	@Test
	public void testPedidoVendido() {
		List<Pieza> piezas = new ArrayList<Pieza>();
		piezas.add(this.pieza);
		piezas.add(this.pieza2);
		Pedido pedido = this.contenedor.crearUnPedido(piezas);
		pedido.efectivizar();
		assertTrue(this.pieza.estaVendida());
		assertTrue(this.pieza2.estaVendida());
		assertTrue(this.pieza3.estaDisponible());
	}

	/**
	 * Testea el estado de las piezas de un pedido cancelado
	 */
	@Test
	public void testPedidoCancelado() {
		List<Pieza> piezas = new ArrayList<Pieza>();
		piezas.add(this.pieza);
		piezas.add(this.pieza2);
		Pedido pedido = this.contenedor.crearUnPedido(piezas);
		pedido.cancelar();
		assertTrue(this.pieza.estaDisponible());
		assertTrue(this.pieza2.estaDisponible());
		assertTrue(this.pieza3.estaDisponible());
	}

	/**
	 * Testea que no se pueda vender un pedido con una pieza disponible (en la
	 * practica no deberia darse un caso como este)
	 */
	@Test(expected = NoPuedoVenderPiezaDisponibleException.class)
	public void testVenderUnPedidoConUnaPiezaDisponible() {
		List<Pieza> piezas = new ArrayList<Pieza>();
		piezas.add(this.pieza);
		piezas.add(this.pieza2);
		Pedido pedido = this.contenedor.crearUnPedido(piezas);
		this.pieza.cambiarEstado(PiezaDisponibleState.getInstance());
		pedido.efectivizar();
	}

	/**
	 * Testea que no se pueda vender un pedido con una pieza ya vendida (en la
	 * practica no deberia darse un caso como este)
	 */
	@Test(expected = YaEstaVendidaException.class)
	public void testVenderUnaPiezaVendida() {
		List<Pieza> piezas = new ArrayList<Pieza>();
		piezas.add(this.pieza);
		piezas.add(this.pieza2);
		Pedido pedido = this.contenedor.crearUnPedido(piezas);
		this.pieza.cambiarEstado(PiezaVendidaState.getInstance());
		pedido.efectivizar();
	}

	/**
	 * Testea que la venta del pedido tenga la cantidad correcta
	 */
	@Test
	public void testPedidoVendidoCantidadPiezas() {
		List<Pieza> piezas = new ArrayList<Pieza>();
		piezas.add(this.pieza);
		piezas.add(this.pieza2);
		Pedido pedido = this.contenedor.crearUnPedido(piezas);
		pedido.efectivizar();
		assertEquals(2, pedido.getCantidadPiezas().intValue());
	}

	/**
	 * Testea que la reserva del pedido tenga la cantidad correcta
	 */
	@Test
	public void testPedidoReservadoCantidadPiezas() {
		List<Pieza> piezas = new ArrayList<Pieza>();
		piezas.add(this.pieza);
		piezas.add(this.pieza2);
		piezas.add(this.pieza3);
		Pedido pedido = this.contenedor.crearUnPedido(piezas);
		assertEquals(3, pedido.getCantidadPiezas().intValue());
	}

	/**
	 * Testea que el pedido cancelado no pueda pedirle la cantidad de piezas
	 */
	@Test(expected = SoyUnPedidoCanceladoException.class)
	public void testPedidoCanceladoCantidadPiezas() {
		List<Pieza> piezas = new ArrayList<Pieza>();
		piezas.add(this.pieza);
		piezas.add(this.pieza2);
		Pedido pedido = this.contenedor.crearUnPedido(piezas);
		pedido.cancelar();
		assertEquals(123, pedido.getCantidadPiezas().intValue());
	}

	/**
	 * Testea que el pedido cancelado no pueda agregar mas piezas
	 */
	@Test(expected = SoyUnPedidoCanceladoException.class)
	public void testPedidoCanceladoAgregarPieza() {
		List<Pieza> piezas = new ArrayList<Pieza>();
		piezas.add(this.pieza);
		piezas.add(this.pieza2);
		Pedido pedido = this.contenedor.crearUnPedido(piezas);
		pedido.cancelar();
		pedido.agregarPieza(this.pieza3);
	}

	/**
	 * Testea que el pedido reservado pueda agregar mas piezas
	 */
	@Test
	public void testPedidoReservadoAgregarPieza() {
		List<Pieza> piezas = new ArrayList<Pieza>();
		piezas.add(this.pieza);
		piezas.add(this.pieza2);
		Pedido pedido = this.contenedor.crearUnPedido(piezas);
		assertEquals(2, pedido.getCantidadPiezas().intValue());
		pedido.agregarPieza(this.pieza3);
		assertEquals(3, pedido.getCantidadPiezas().intValue());
	}

	/**
	 * Testea que el pedido vendido no pueda agregar mas piezas
	 */
	@Test(expected = SoyUnPedidoVendidoException.class)
	public void testPedidoVendidoAgregarPieza() {
		List<Pieza> piezas = new ArrayList<Pieza>();
		piezas.add(this.pieza);
		piezas.add(this.pieza2);
		Pedido pedido = this.contenedor.crearUnPedido(piezas);
		pedido.efectivizar();
		pedido.agregarPieza(this.pieza3);
	}

	/**
	 * Test de ruta de log variable
	 */
	@Test
	public void testRutaLog() {
		List<Pieza> piezas = new ArrayList<Pieza>();
		piezas.add(this.pieza);
		piezas.add(this.pieza2);
		Pedido pedido = this.contenedor.crearUnPedido(piezas);
		pedido.cancelar();
		pedido = this.contenedor.crearUnPedido(piezas);
		pedido.efectivizar();
	}

	/**
	 * Ver por consola..
	 */
	@Test
	public void testLogLevel() {
		try {
			List<Pieza> piezas = new ArrayList<Pieza>();
			piezas.add(this.pieza);
			piezas.add(this.pieza2);
			Pedido pedido = this.contenedor.crearUnPedido(piezas);
			pedido.efectivizar();
			pedido.agregarPieza(this.pieza3);// Tira exception pero no loguea
		} catch (Throwable t) {
		}
		try {
			List<Pieza> piezas = new ArrayList<Pieza>();
			piezas.add(this.pieza);
			this.pieza.cambiarEstado(PiezaDisponibleState.getInstance());
			piezas.add(this.pieza2);
			this.pieza2.cambiarEstado(PiezaDisponibleState.getInstance());
			Pedido pedido = this.contenedor.crearUnPedido(piezas);
			pedido.efectivizar();
			pedido.agregarPieza(this.pieza3);// Tira exception y la loguea
		} catch (Throwable t) {
		}
	}
}
